/**
 * 聊天相关 API 工具方法（含统一的 SSE 解析流程）
 */

import apiClient from './api'
import ErrorHandler from '@/utils/errorHandler'

export interface Message {
  role: 'user' | 'assistant' | 'system'
  content: string
}

export interface ChatConfig {
  model?: string
  temperature?: number
  maxTokens?: number
  topP?: number
  useRAG?: boolean
  useTools?: boolean
}

export interface ChatRequest {
  sessionId: string
  messages: Message[]
  stream: boolean
  chatConfig?: ChatConfig
}

export interface ChatListeners {
  onChunk: (chunk: string) => void
  onComplete?: () => void
  onError?: (error: Error) => void
  onWarning?: (message: string) => void
}

type SSEEventType = 'chunk' | 'done' | 'warning' | 'error'

interface SSEPayload {
  type: SSEEventType
  content?: string
  [key: string]: unknown
}

export const API_ENDPOINTS = {
  CHAT: '/v1/chat'
}

const resolveApiBase = (): string => {
  const envBase = (import.meta as any)?.env?.VITE_API_BASE_URL as string | undefined
  const clientBase = (apiClient.defaults.baseURL as string | undefined) ?? ''
  const fallback =
    typeof window !== 'undefined' && window.location?.origin ? window.location.origin : ''
  const resolved = (envBase || clientBase || fallback || '').trim()
  return resolved.replace(/\/$/, '')
}

interface HandleEventContext {
  listeners: ChatListeners
  warningsShown: Set<string>
  closeStream: (cb?: () => void) => void
}

const parseEventData = (rawFrame: string): string | null => {
  const dataLine = rawFrame.split('\n').find((line) => line.startsWith('data:'))
  if (!dataLine) return null
  return dataLine.replace(/^data:\s*/, '')
}

const handleSSEEvent = (data: string, ctx: HandleEventContext): void => {
  if (!data.trim()) return

  let payload: SSEPayload
  try {
    payload = JSON.parse(data)
  } catch {
    ctx.listeners.onChunk(data)
    return
  }

  switch (payload.type) {
    case 'chunk':
      if (payload.content) {
        ctx.listeners.onChunk(payload.content)
      }
      break
    case 'warning':
      if (payload.content && !ctx.warningsShown.has(payload.content)) {
        ctx.warningsShown.add(payload.content)
        if (ctx.listeners.onWarning) {
          ctx.listeners.onWarning(payload.content)
        } else {
          ErrorHandler.showWarning(payload.content, '流式响应')
        }
      }
      break
    case 'done':
      ctx.closeStream(() => ctx.listeners.onComplete?.())
      break
    case 'error':
      ctx.closeStream(() => ctx.listeners.onError?.(new Error(payload.content || 'Stream error')))
      break
    default:
      if (typeof payload.content === 'string') {
        ctx.listeners.onChunk(payload.content)
      }
      break
  }
}

const buildChatPayload = (request: ChatRequest): Record<string, unknown> => {
  const payload: Record<string, unknown> = {
    session_id: request.sessionId,
    messages: request.messages,
    stream: request.stream
  }

  const lastUserMessage = [...request.messages].reverse().find((msg) => msg.role === 'user')
  if (lastUserMessage) {
    payload.message = lastUserMessage.content
  }

  if (request.chatConfig) {
    const { model, temperature, maxTokens, topP, useRAG, useTools } = request.chatConfig
    if (model) payload.model = model
    if (typeof temperature === 'number') payload.temperature = temperature
    if (typeof maxTokens === 'number') payload.max_tokens = maxTokens
    if (typeof topP === 'number') payload.top_p = topP
    if (typeof useRAG === 'boolean') payload.use_rag = useRAG
    if (typeof useTools === 'boolean') payload.use_tools = useTools
  }

  return payload
}

export const chat = (
  request: ChatRequest,
  listeners: ChatListeners,
  externalSignal?: AbortSignal
): AbortController => {
  const controller = new AbortController()
  if (externalSignal) {
    if (externalSignal.aborted) {
      controller.abort()
    } else {
      externalSignal.addEventListener('abort', () => controller.abort(), { once: true })
    }
  }

  const payload = buildChatPayload(request)
  const timeoutMs = 60000
  let timeoutId: number | null = null

  const startTimeout = () => {
    if (timeoutId) clearTimeout(timeoutId)
    timeoutId = window.setTimeout(() => {
      controller.abort()
      listeners.onError?.(new Error('聊天请求超时'))
    }, timeoutMs)
  }

  const clearTimeoutSafely = () => {
    if (timeoutId) {
      clearTimeout(timeoutId)
      timeoutId = null
    }
  }

  const closeController = () => {
    if (!controller.signal.aborted) {
      controller.abort()
    }
  }

  const runNonStreamRequest = async () => {
    try {
      startTimeout()
      const response = await fetch(`${resolveApiBase()}${API_ENDPOINTS.CHAT}`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Accept: 'application/json',
          ...(localStorage.getItem('authToken')
            ? { Authorization: `Bearer ${localStorage.getItem('authToken')}` }
            : {})
        },
        body: JSON.stringify(payload),
        signal: controller.signal
      })

      if (!response.ok) {
        const text = await response.text()
        throw new Error(`HTTP ${response.status}: ${text || 'unknown error'}`)
      }
      const data = await response.json()
      const content = data?.content || data?.result || ''
      if (content) {
        listeners.onChunk(content)
      }
      listeners.onComplete?.()
    } catch (error) {
      if ((error as Error).name !== 'AbortError') {
        ErrorHandler.handleError(error, '聊天请求', 'auto')
      }
      listeners.onError?.(error as Error)
    } finally {
      clearTimeoutSafely()
    }
  }

  const processSSEStream = async () => {
    let reader: ReadableStreamDefaultReader<Uint8Array> | null = null
    try {
      const response = await fetch(`${resolveApiBase()}${API_ENDPOINTS.CHAT}`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Accept: 'text/event-stream',
          ...(localStorage.getItem('authToken')
            ? { Authorization: `Bearer ${localStorage.getItem('authToken')}` }
            : {})
        },
        body: JSON.stringify(payload),
        signal: controller.signal
      })

      if (!response.ok) {
        const errorText = await response.text()
        throw new Error(`HTTP ${response.status}: ${errorText || 'unknown error'}`)
      }
      if (!response.body) {
        throw new Error('Empty response body')
      }

      reader = response.body.getReader()
      const decoder = new TextDecoder('utf-8')
      let buffer = ''
      let streamClosed = false

      const ctx: HandleEventContext = {
        listeners,
        warningsShown: new Set<string>(),
        closeStream: (cb?: () => void) => {
          if (streamClosed) return
          streamClosed = true
          clearTimeoutSafely()
          cb?.()
          closeController()
        }
      }

      startTimeout()

      while (!streamClosed) {
        const { done, value } = await reader.read()
        if (done) {
          ctx.closeStream(() => listeners.onComplete?.())
          break
        }

        startTimeout()
        buffer += decoder.decode(value, { stream: true })

        while (!streamClosed) {
          const delimiterIndex = buffer.indexOf('\n\n')
          if (delimiterIndex === -1) break

          const frame = buffer.slice(0, delimiterIndex)
          buffer = buffer.slice(delimiterIndex + 2)

          if (!frame.trim()) continue

          const data = parseEventData(frame)
          if (!data) continue

          handleSSEEvent(data, ctx)
        }
      }
    } catch (error) {
      if ((error as Error).name !== 'AbortError') {
        ErrorHandler.handleError(error, '聊天流', 'auto')
      }
      listeners.onError?.(error as Error)
    } finally {
      clearTimeoutSafely()
      try {
        await reader?.cancel()
      } catch {
        /* noop */
      }
      closeController()
    }
  }

  if (request.stream) {
    processSSEStream()
  } else {
    runNonStreamRequest()
  }

  return controller
}

export const createSession = async (): Promise<{ session_id: string; title: string }> => {
  try {
    const response = await apiClient.post('/v1/sessions', {})
    return response.data
  } catch (error) {
    ErrorHandler.handle(error, '创建会话失败')
    throw error
  }
}

export const deleteSession = async (sessionId: string): Promise<void> => {
  try {
    await apiClient.delete(`/v1/sessions/${sessionId}`)
  } catch (error) {
    ErrorHandler.handle(error, '删除会话失败')
    throw error
  }
}

export const getSessions = async (): Promise<any[]> => {
  try {
    const response = await apiClient.get('/v1/sessions')
    return response.data?.sessions ?? []
  } catch (error) {
    ErrorHandler.handle(error, '获取会话列表失败')
    throw error
  }
}

export const getChatHistory = async (
  sessionId: string,
  params: { limit?: number; offset?: number } = {}
): Promise<any> => {
  try {
    const response = await apiClient.get(`/v1/sessions/${sessionId}/messages`, { params })
    return response.data
  } catch (error) {
    ErrorHandler.handle(error, '获取聊天历史失败')
    throw error
  }
}
